using System;
using System.IO;
using System.Diagnostics;
using MSHelpLib.BaseCode;

namespace MSHelpLib
{
	internal class lzx_bits 
	{
		public UInt32 bb=0;
		public int bl=0;			
	}

	internal class lzw
	{		
		private int ULONG_BITS_VALUE=32;

		public lzw()
		{		
			ULONG_BITS_VALUE=ULONG_BITS();
		}

		/* $Id: lzx.c,v 1.5 2002/10/09 01:16:33 jedwin Exp $ */
		/***************************************************************************
		*                        lzx.c - LZX decompression routines               *
		*                           -------------------                           *
		*                                                                         *
		*  maintainer: Jed Wing <jedwin@ugcs.caltech.edu>                         *
		*  source:     modified lzx.c from cabextract v0.5                        *
		*  notes:      This file was taken from cabextract v0.5, which was,       *
		*              itself, a modified version of the lzx decompression code   *
		*              from unlzx.                                                *
		*                                                                         *
		*  platforms:  In its current incarnation, this file has been tested on   *
		*              two different Linux platforms (one, redhat-based, with a   *
		*              2.1.2 glibc and gcc 2.95.x, and the other, Debian, with    *
		*              2.2.4 glibc and both gcc 2.95.4 and gcc 3.0.2).  Both were *
		*              Intel x86 compatible machines.                             *
		***************************************************************************/

		/***************************************************************************
		*                                                                         *
		*   This program is free software; you can redistribute it and/or modify  *
		*   it under the terms of the GNU General Public License as published by  *
		*   the Free Software Foundation; either version 2 of the License, or     *
		*   (at your option) any later version.  Note that an exemption to this   *
		*   license has been granted by Stuart Caie for the purposes of           *
		*   distribution with CHMFile.  This does not, to the best of my           *
		*   knowledge, constitute a change in the license of this (the LZX) code  *
		*   in general.                                                           *
		*                                                                         *
		***************************************************************************/

		/* some constants defined by the LZX specification */
		private  const int  LZX_MIN_MATCH                = 2;
		private  const int  LZX_MAX_MATCH                = 257;
		private  const int  LZX_NUM_CHARS                = 256;
		private  const int  LZX_BLOCKTYPE_INVALID        = 0;   /* also blocktypes 4-7 invalid */
		private  const int  LZX_BLOCKTYPE_VERBATIM       = 1;
		private  const int  LZX_BLOCKTYPE_ALIGNED        = 2;
		private  const int  LZX_BLOCKTYPE_UNCOMPRESSED   = 3;
		private  const int  LZX_PRETREE_NUM_ELEMENTS     = 20;
		private  const int  LZX_ALIGNED_NUM_ELEMENTS     = 8;   /* aligned offset tree #elements */
		private  const int  LZX_NUM_PRIMARY_LENGTHS      = 7;   /* this one missing from spec! */
		private  const int  LZX_NUM_SECONDARY_LENGTHS    = 249; /* length tree #elements */

		/* LZX huffman defines: tweak tablebits as desired */
		private  const int  LZX_PRETREE_MAXSYMBOLS  = LZX_PRETREE_NUM_ELEMENTS;
		private  const int  LZX_PRETREE_TABLEBITS   = 6;
		private  const int  LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 50*8;
		private  const int  LZX_MAINTREE_TABLEBITS  = 12;
		private  const int  LZX_LENGTH_MAXSYMBOLS   = LZX_NUM_SECONDARY_LENGTHS+1;
		private  const int  LZX_LENGTH_TABLEBITS    = 12;
		private  const int  LZX_ALIGNED_MAXSYMBOLS  = LZX_ALIGNED_NUM_ELEMENTS;
		private  const int  LZX_ALIGNED_TABLEBITS   = 7;
		private  const int  LZX_LENTABLE_SAFETY	  = 64; /* we allow length table decoding overruns */

		public	 const int  DECR_OK				= 0;
		public	 const int  DECR_DATAFORMAT		= 1;
		public	 const int  DECR_ILLEGALDATA	= 2;
		public	 const int  DECR_NOMEMORY		= 3;

		private  byte[] window;         /* the actual decoding window              */
		private  ulong window_size;     /* window size (32Kb through 2Mb)          */
		private  ulong actual_size;     /* window size when it was first allocated */
		private  ulong window_posn;     /* current offset within the window        */
		private  ulong R0, R1, R2;      /* for the LRU offset system               */
		private  UInt32 main_elements;   /* number of main tree elements            */
		private  int   header_read;     /* have we started decoding at all yet?    */
		private  UInt32 block_type;      /* type of this block                      */
		private  ulong block_length;    /* uncompressed length of this block       */
		private  ulong block_remaining; /* uncompressed bytes still left to decode */
		private  ulong frames_read;     /* the number of CFDATA blocks processed   */
		private  long  intel_filesize;  /* magic header value used for transform   */
		private  long  intel_curpos;    /* current offset in transform space       */
		private  int   intel_started;   /* have we seen any translatable data yet? */


		private  uint [] PRETREE_table = new uint[(1<<(6)) + (((20))<<1)]; 
		private  byte [] PRETREE_len = new byte [((20)) + (64)];

		private  uint [] MAINTREE_table= new uint[(1<<(12)) + (((256) + 50*8)<<1)]; 
		private  byte [] MAINTREE_len = new byte [((256) + 50*8) + (64)];

		private  uint [] LENGTH_table= new uint[(1<<(12)) + (((249)+1)<<1)]; 
		private  byte [] LENGTH_len = new byte [((249)+1) + (64)];

		private  uint [] ALIGNED_table= new uint[(1<<(7)) + (((8))<<1)]; 
		private  byte [] ALIGNED_len = new byte [((8)) + (64)];		
		private BinaryReaderEx BitSource=null;
		private System.IO.Stream OutputStream=null;

		/* LZX decruncher */

		/* Microsoft's LZX document and their implementation of the
		* com.ms.util.cab Java package do not concur.
		*
		* In the LZX document, there is a table showing the correlation between
		* window size and the number of position slots. It states that the 1MB
		* window = 40 slots and the 2MB window = 42 slots. In the implementation,
		* 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the
		* first slot whose position base is equal to or more than the required
		* window size'. This would explain why other tables in the document refer
		* to 50 slots rather than 42.
		*
		* The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
		* is not defined in the specification.
		*
		* The LZX document does not state the uncompressed block has an
		* uncompressed length field. Where does this length field come from, so
		* we can know how large the block is? The implementation has it as the 24
		* bits following after the 3 blocktype bits, before the alignment
		* padding.
		*
		* The LZX document states that aligned offset blocks have their aligned
		* offset huffman tree AFTER the main and length trees. The implementation
		* suggests that the aligned offset tree is BEFORE the main and length
		* trees.
		*
		* The LZX document decoding algorithm states that, in an aligned offset
		* block, if an extra_bits value is 1, 2 or 3, then that number of bits
		* should be read and the result added to the match offset. This is
		* correct for 1 and 2, but not 3, where just a huffman symbol (using the
		* aligned tree) should be read.
		*
		* Regarding the E8 preprocessing, the LZX document states 'No translation
		* may be performed on the last 6 bytes of the input block'. This is
		* correct.  However, the pseudocode provided checks for the *E8 leader*
		* up to the last 6 bytes. If the leader appears between -10 and -7 bytes
		* from the end, this would cause the next four bytes to be modified, at
		* least one of which would be in the last 6 bytes, which is not allowed
		* according to the spec.
		*
		* The specification states that the huffman trees must always contain at
		* least one element. However, many CAB files contain blocks where the
		* length tree is completely empty (because there are no matches), and
		* this is expected to succeed.
		*/

		/* LZX uses what it calls 'position slots' to represent match offsets.
		* What this means is that a small 'position slot' number and a small
		* offset from that slot are encoded instead of one large offset for
		* every match.
		* - position_base is an index to the position slot bases
		* - extra_bits states how many bits of offset-from-base data is needed.
		*/
		private byte [] extra_bits = {
										 0,  0,  0,  0,  1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,
										 7,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
										 15, 15, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
										 17, 17, 17
									 };

		private ulong [] position_base = {
											 0,       1,       2,      3,      4,      6,      8,     12,     16,     24,     32,       48,      64,      96,     128,     192,
											 256,     384,     512,    768,   1024,   1536,   2048,   3072,   4096,   6144,   8192,    12288,   16384,   24576,   32768,   49152,
											 65536,   98304,  131072, 196608, 262144, 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
											 1835008, 1966080, 2097152
										 };
		
		private UInt32 ReadUInt16()
		{			
			if (BitSource.BaseStream.Position>=BitSource.BaseStream.Length)
				return 0;

            if (BitSource.BaseStream.Position + 1 >= BitSource.BaseStream.Length)
            {
                return BitSource.ReadByte();                
            }
            else
            {
                return BitSource.ReadUInt16();
            }						
		}

		public bool LZXinit(int WindowSize)
		{			
			ulong wndsize = (ulong)(1 << WindowSize);
			int i, posn_slots;

			/* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */
			/* if a previously allocated window is big enough, keep it     */
			if (WindowSize< 15 || WindowSize> 21) return false;

			/* allocate state and associated window */		
			window = new byte[wndsize];
			if (window==null)
			{					
				return false;
			}

			actual_size = wndsize;
			window_size = wndsize;

			/* calculate required position slots */
			if (WindowSize == 20) posn_slots = 42;
			else if (WindowSize== 21) posn_slots = 50;
			else posn_slots = WindowSize << 1;

			/* alternatively */
			/* posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */

			/* initialize other state */
			R0  =  R1  = R2 = 1;
			main_elements   = (uint)(LZX_NUM_CHARS + (posn_slots << 3));
			header_read     = 0;
			frames_read     = 0;
			block_remaining = 0;
			block_type      = LZX_BLOCKTYPE_INVALID;
			intel_curpos    = 0;
			intel_started   = 0;
			window_posn     = 0;

			/* initialise tables to 0 (because deltas will be applied to them) */
			for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) MAINTREE_len[i] = 0;
			for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)   LENGTH_len[i]   = 0;			

			return true;
		}

		public void LZXteardown()
		{
			window=null;			
		}

		public int LZXreset()
		{
			R0  =  R1  = R2 = 1;
			header_read     = 0;
			frames_read     = 0;
			block_remaining = 0;
			block_type      = LZX_BLOCKTYPE_INVALID;
			intel_curpos    = 0;
			intel_started   = 0;
			window_posn     = 0;

			for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) MAINTREE_len[i] = 0;
			for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++)   LENGTH_len[i]   = 0;

			return DECR_OK;
		}


		/* Bitstream reading macros:
		*
		* INIT_BITSTREAM    should be used first to set up the system
		* READ_BITS(var,n)  takes N bits from the buffer and puts them in var
		*
		* ENSURE_BITS(n)    ensures there are at least N bits in the bit buffer
		* PEEK_BITS(n)      extracts (without removing) N bits from the bit buffer
		* REMOVE_BITS(n)    removes N bits from the bit buffer
		*
		* These bit access routines work by using the area beyond the MSB and the
		* LSB as a free source of zeroes. This avoids having to mask any bits.
		* So we have to know the bit width of the bitbuffer variable. This is
		* sizeof(ulong) * 8, also defined as ULONG_BITS
		*/

		/* number of bits in ulong. Note: This must be at multiple of 16, and at
		* least 32 for the bitbuffer code to work (ie, it must be able to ensure
		* up to 17 bits - that's adding 16 bits when there's one bit left, or
		* adding 32 bits when there are no bits left. The code should work fine
		* for machines where ulong >= 32 bits.
		*/
		private int ULONG_BITS()
		{
			int rc=(System.Runtime.InteropServices.Marshal.SizeOf(typeof(System.UInt32))<<3);
			return rc;
		}

		/* make_decode_table(nsyms, nbits, length[], table[])
		*
		* This function was coded by David Tritscher. It builds a fast huffman
		* decoding table out of just a canonical huffman code lengths table.
		*
		* nsyms  = total number of symbols in this huffman tree.
		* nbits  = any symbols with a code length of nbits or less can be decoded
		*          in one lookup of the table.
		* length = A table to get code lengths from [0 to syms-1]
		* table  = The table to fill up with decoded symbols and pointers.
		*
		* Returns 0 for OK or 1 for error
		*/

		private int make_decode_table(ulong nsyms, byte nbits, ref byte [] length, ref UInt32[] table) 
		{
			ulong sym;
			ulong leaf;
			byte bit_num = 1;
			ulong fill;
			ulong pos         = 0; /* the current position in the decode table */
			ulong table_mask  = (ulong)(1 << nbits);
			ulong bit_mask    = table_mask >> 1; /* don't do 0 length codes */
			ulong next_symbol = bit_mask; /* base of allocation for long codes */

			/* fill entries for codes short enough for a direct mapping */
			while (bit_num <= nbits) 
			{
				for (sym = 0; sym < nsyms; sym++) 
				{
					if (length[sym] == bit_num) 
					{
						leaf = pos;

						if((pos += bit_mask) > table_mask) return 1; /* table overrun */

						/* fill all possible lookups of this symbol with the symbol itself */
						fill = bit_mask;
						while (fill-- > 0) table[leaf++] = (uint)sym;
					}
				}
				bit_mask >>= 1;
				bit_num++;
			}

			/* if there are any codes longer than nbits */
			if (pos != table_mask) 
			{
				/* clear the remainder of the table */
				for (sym = pos; sym < table_mask; sym++) table[sym] = 0;

				/* give ourselves room for codes to grow by up to 16 more bits */
				pos <<= 16;
				table_mask <<= 16;
				bit_mask = 1 << 15;

				while (bit_num <= 16) 
				{
					for (sym = 0; sym < nsyms; sym++) 
					{
						if (length[sym] == bit_num) 
						{
							leaf = pos >> 16;
							for (fill = 0; fill < (ulong)(bit_num - nbits); fill++) 
							{
								/* if this path hasn't been taken yet, 'allocate' two entries */
								if (table[leaf] == 0) 
								{
									table[(next_symbol << 1)] = 0;
									table[(next_symbol << 1) + 1] = 0;
									table[leaf] = (uint)next_symbol++;
								}
								/* follow the path and select either left or right for next bit */
								leaf = table[leaf] << 1;
								if (((pos >> (byte)(15-fill)) & 1)==1) 
									leaf++;
							}
							table[leaf] = (uint)sym;

							if ((pos += bit_mask) > table_mask) 
								return 1; /* table overflow */
						}
					}
					bit_mask >>= 1;
					bit_num++;
				}
			}

			/* full table? */
			if (pos == table_mask) 
				return 0;

			/* either erroneous table, or all elements are 0 - let's find out. */
			for (sym = 0; sym < nsyms; sym++) if (length[(uint)sym]!=0) 
												  return 1;

			return 0;
		}		

		private int lzx_read_lens(byte []lens, ulong first, ulong last, ref lzx_bits lb) 
		{
			ulong i,j, x,y;
			int z;
			
			UInt32 bitbuf = lb.bb;
			int bitsleft = lb.bl;			

			UInt32 [] hufftbl=null;			

			for (x = 0; x < 20; x++) 
			{
				do
				{
				while (bitsleft < (4)) 
				{ 
					bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
					bitsleft += 16; 
				} 
					y = (bitbuf >> (ULONG_BITS_VALUE- (4))); 
					bitbuf <<= 4; 
					bitsleft -= 4; 
				}
				while (false);
				PRETREE_len[x] = (byte)y;
			}	
			if (make_decode_table( 20, 6, ref PRETREE_len, ref PRETREE_table)!=0)
				return 2;			

			for (x = first; x < last; ) 
			{
				do
				{
				while (bitsleft < 16) 
				{ 
					bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
					bitsleft += 16; 
				} 
					hufftbl = PRETREE_table; 
					if ((i = hufftbl[((ulong)bitbuf >> (ULONG_BITS_VALUE- 6))]) >= 20) 
					{ 
						j = (ulong)(1 << (byte)(ULONG_BITS_VALUE- ((6)))); 
						do 
						{
							j >>= 1; 
							i <<= 1; 
							if ((bitbuf & j)!=0)
								i|=1;
							else
								i|=0;

							if (j==0) 
							{
								return (2); 
							} 
						} 
						while ((i = hufftbl[i]) >= 20); 
					} 
					z = (int)i;
					j = PRETREE_len[z]; 
					bitbuf <<= (byte)j;
					bitsleft -= (int)j; 
				}
				while (false);

				if (z == 17) 
				{
					do
					{
					while (bitsleft < (4)) 
					{ 
						bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
						bitsleft += 16; 
					} 
						y = (bitbuf >> (ULONG_BITS_VALUE- (4))); 
						bitbuf <<= 4;
						bitsleft -= 4; 
					}
					while(false);
					y += 4;
				
					while ((y--)!=0) 
						lens[x++] = 0;
				}
				else if (z == 18) 
				{					
					do
					{
					while (bitsleft < (5)) 
					{ 
						bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
						bitsleft += 16;
					} 
						(y) = (bitbuf >> (ULONG_BITS_VALUE- (5))); 
						bitbuf <<= 5;
						bitsleft -= 5; 
					}
					while (false);
				
					y += 20;

					while ((y--)!=0) 
						lens[x++] = 0;
				}
				else if (z == 19) 
				{
					do
					{
					while (bitsleft < (1)) 
					{
						bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
						bitsleft += 16; 
					} 
						y = (bitbuf >> (ULONG_BITS_VALUE- (1))); 
						bitbuf <<= 1;
						bitsleft -= 1; 
					}
					while(false);
					y += 4;
					do
					{
					while (bitsleft < (16)) 
					{ 
						bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
						bitsleft += 16; 							
					} 
						hufftbl = (PRETREE_table); 
						if ((i = hufftbl[(bitbuf >> (ULONG_BITS_VALUE- 6))]) >= 20) 
						{ 
							j = (ulong)1 << (byte)(ULONG_BITS_VALUE- 6); 
							do 
							{ 
								j >>= 1; 
								i <<= 1; 
								if ((bitbuf & j)==0)
									i|=0;
								else
									i|=1;
								if (j==0) 
								{ 
									return (2); 
								} 
							} 
							while ((i = hufftbl[i]) >= 20); 
						} 
						z = (int)i;
						j = PRETREE_len[z]; 					

						bitbuf <<= (byte)j;
						bitsleft -= (int)j; 
					}
					while(false);
					z = lens[x] - z; 
					if (z < 0) 
						z += 17;

					while ((y--)!=0) 
						lens[x++] = (byte)z;
				}
				else 
				{
					z = lens[x] - z; 
					if (z < 0) 
						z += 17;
					lens[x++] = (byte)z;
				}
			}
			lb.bb = bitbuf;
			lb.bl = bitsleft;
			return 0;
		}

		public int LZXdecompress(BinaryReaderEx inpos, System.IO.Stream outpos, ref ulong inlen, ref ulong outlen) 
		{
			BitSource=inpos;
			OutputStream=outpos;
			
			long endinp = BitSource.Position+(long)inlen;						
			ulong runsrc, rundest;
			UInt32 [] hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */

			UInt32 bitbuf;
			int bitsleft;
			ulong match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */
			lzx_bits lb; /* used in READ_LENGTHS macro */
			lb=new lzx_bits();			

			int togo = (int)outlen, this_run, main_element, aligned_bits;
			int match_length, length_footer, extra, verbatim_bits;

			bitsleft = 0; 
			bitbuf = 0; 

			/* read header if necessary */
			if (header_read==0) 
			{
				i = j = 0;	
				do
				{
				while (bitsleft < (1)) 
				{ 
					bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
					bitsleft += 16; 
				} 
					k = (bitbuf >> (ULONG_BITS_VALUE- (1))); 
					bitbuf <<= 1;
					bitsleft -= 1;
				}
				while(false);

				if (k!=0)	
				{
					do
					{
					while (bitsleft < (16)) 
					{ 
						bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE -16 -  bitsleft); 
						bitsleft += 16; 
					} 
						i = (bitbuf >> (ULONG_BITS_VALUE- (16))); 
						bitbuf <<= 16;
						bitsleft -= 1;
					}
					while(false);

					do
					{
					while (bitsleft < (16)) 
					{ 
						bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
						bitsleft += 16; 							
					} 
						j = (bitbuf >> (ULONG_BITS_VALUE- (16))); 
						bitbuf <<= 16;
						bitsleft -= 16;					
					}
					while(false);
				}
				intel_filesize = (long)((i << 16) | j); 
				header_read = 1;
			}

			/* main decoding loop */
			while (togo > 0) 
			{
				if (block_remaining == 0) 
				{
					if (block_type == (3)) 
					{
						if ((block_length & 1)!=0) 
							BitSource.ReadByte();
						bitsleft = 0; 
						bitbuf = 0; 
					}

					do 
					{ 
					while (bitsleft < (3)) 
					{ 
						bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
						bitsleft += 16; 
					} 
						(block_type) = (uint)(bitbuf >> (ULONG_BITS_VALUE- (3))); 
						bitbuf <<= 3; 
						bitsleft -= 3; 
					} 
					while (false);

					do 
					{ 
					while (bitsleft < (16)) 
					{ 
						bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
						bitsleft += 16; 
					} 
						(i) = (bitbuf >> (ULONG_BITS_VALUE- (16))); 
						bitbuf <<= 16;  
						bitsleft -= 16; 
					} 
					while (false);

					do 
					{ 
					while (bitsleft < (8)) 
					{ 
						bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
						bitsleft += 16; 
					} 
						(j) = (bitbuf >> (ULONG_BITS_VALUE- (8))); 
						bitbuf <<= 8; 
						bitsleft -= 8; 
					} 
					while (false);
					block_remaining = block_length = (i << 8) | j;

					switch (block_type) 
					{
						case (LZX_BLOCKTYPE_ALIGNED):
							for (i = 0; i < 8; i++) 
							{ 
								do 
								{ 
								while (bitsleft < (3)) 
								{ 
									bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
									bitsleft += 16; 
								} 
									(j) = (bitbuf >> (ULONG_BITS_VALUE- (3))); 
									bitbuf <<= 3; 
									bitsleft -= 3; 
								} 
								while (false); 
								(ALIGNED_len)[i] = (byte)j; 
							}
							if (make_decode_table( 8, 7, ref ALIGNED_len, ref ALIGNED_table)!=0) 
							{ 
								return (2); 
							}

							do 
							{ 
								lb.bb = bitbuf; 
								lb.bl = bitsleft; 								
								if (lzx_read_lens(MAINTREE_len,0,256,ref lb)!=0) 
								{ 
									return (2); 
								} 
								bitbuf = lb.bb; 
								bitsleft = lb.bl; 								
							} 
							while (false);
							do 
							{ 
								lb.bb = bitbuf; 
								lb.bl = bitsleft; 
								if (lzx_read_lens(MAINTREE_len,256,main_elements,ref lb)!=0) 
								{ 
									return (2); 
								} 
								bitbuf = lb.bb; 
								bitsleft = lb.bl; 
							} 
							while (false);

							if (make_decode_table( (256 + 50*8), 12, ref MAINTREE_len, ref MAINTREE_table)!=0) 
							{ 
								return (2); 
							}
							if (MAINTREE_len[0xE8] != 0) intel_started = 1;

							do 
							{ 
								lb.bb = bitbuf; 
								lb.bl = bitsleft; 
								if (lzx_read_lens(LENGTH_len,0,249,ref lb)!=0) 
								{ 
									return (2); 
								} 
								bitbuf = lb.bb; 
								bitsleft = lb.bl; 
							} 
							while (false);
							if (make_decode_table( (249+1), 12, ref LENGTH_len, ref LENGTH_table)!=0) 
							{ 
								return (2); 
							}
							break;							
		                    
						case (LZX_BLOCKTYPE_VERBATIM):
							do 
							{ 
								lb.bb = bitbuf; 
								lb.bl = bitsleft; 								
								if (lzx_read_lens(MAINTREE_len,0,256,ref lb)!=0) 
								{ 
									return (2); 
								} 
								bitbuf = lb.bb; 
								bitsleft = lb.bl; 								
							} 
							while (false);
							do 
							{ 
								lb.bb = bitbuf; 
								lb.bl = bitsleft; 								
								if (lzx_read_lens(MAINTREE_len,256,main_elements,ref lb)!=0) 
								{ 
									return (2); 
								} 
								bitbuf = lb.bb; 
								bitsleft = lb.bl; 
							} 
							while (false);

							if (make_decode_table( (256 + 50*8), 12, ref MAINTREE_len, ref MAINTREE_table)!=0) 
							{ 
								return (2); 
							}
							if (MAINTREE_len[0xE8] != 0) intel_started = 1;

							do 
							{ 
								lb.bb = bitbuf; 
								lb.bl = bitsleft; 
								if (lzx_read_lens(LENGTH_len,0,249,ref lb)!=0) 
								{ 
									return (2); 
								} 
								bitbuf = lb.bb; 
								bitsleft = lb.bl; 
							} 
							while (false);
							if (make_decode_table( (249+1), 12, ref LENGTH_len, ref LENGTH_table)!=0) 
							{ 
								return (2); 
							}
							break;

						case (LZX_BLOCKTYPE_UNCOMPRESSED):
							intel_started = 1; 
							while (bitsleft < (16)) 
							{ 
								bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - bitsleft); 
								bitsleft += 16;
							} 
							if (bitsleft > 16)
							{
								BitSource.Seek(-2,System.IO.SeekOrigin.Current);								
							}
							R0 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24));
							R1 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24));
							R2 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24));
							break;

						default:
							return (DECR_ILLEGALDATA);
					}
				}

				/* buffer exhaustion check */
				if (BitSource.Position > (long) endinp) 
				{
					/* it's possible to have a file where the next run is less than
					* 16 bits in size. In this case, the READ_HUFFSYM() macro used
					* in building the tables will exhaust the buffer, so we should
					* allow for this, but not allow those accidentally read bits to
					* be used (so we check that there are at least 16 bits
					* remaining - in this boundary case they aren't really part of
					* the compressed data)
					*/
					if (BitSource.Position> (long)(endinp+2) || bitsleft < 16) 
						return DECR_ILLEGALDATA;
				}

				while ((this_run = (int)block_remaining) > 0 && togo > 0) 
				{
					if (this_run > togo)
						this_run = togo;

					togo -= this_run;
					block_remaining -= (ulong)this_run;

					/* apply 2^x-1 mask */
					window_posn &= window_size - 1;

					/* runs can't straddle the window wraparound */
					if ((window_posn + (ulong)this_run) > window_size)
						return DECR_DATAFORMAT;

					switch (block_type) 
					{
						case LZX_BLOCKTYPE_VERBATIM:
							while (this_run > 0) 
							{
								do 
								{ 
								while (bitsleft < (16)) 
								{ 
									bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
									bitsleft += 16; 
								} 
									hufftbl = MAINTREE_table; 
									if ((i = hufftbl[(bitbuf >> (ULONG_BITS_VALUE- 12))]) >= 256 + 50*8) 
									{ 
										j = (ulong)(1 << (ULONG_BITS_VALUE- 12)); 
										do 
										{ 
											j >>= 1; 
											i <<= 1; 
											if ((bitbuf & j)!=0) 
												i|=1; 
											else 
												i|=0; 
											if (j==0) 
											{ 
												return (2); 
											} 
										} 
										while ((i = hufftbl[i]) >= (((256) + 50*8))); 
									} 
									j = MAINTREE_len[main_element = (int)i]; 
									bitbuf <<= (byte)j; 
									bitsleft -= (byte)j; 
								} 
								while (false);

								if (main_element < (256)) 
								{	                            
									window[window_posn++] = (byte)main_element;
									this_run--;
								}
								else 
								{	                            
									main_element -= (256);

									match_length = main_element & (7);
									if (match_length == (7)) 
									{
										do 
										{ 
										while (bitsleft < (16)) 
										{ 
											bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
											bitsleft += 16; 
										} 
											hufftbl = (LENGTH_table); 
											if ((i = hufftbl[(bitbuf >> (ULONG_BITS_VALUE- 12))]) >= (((249)+1))) 
											{ 
												j = (ulong)(1 << (ULONG_BITS_VALUE- ((12)))); 
												do 
												{ 
													j >>= 1; 
													i <<= 1; 
													if ((bitbuf & j)!=0) 
														i|=1; 
													else 
														i|=0; 
													if (j==0) 
													{ 
														return (2); 
													} 
												} 
												while ((i = hufftbl[i]) >= (((249)+1))); 
											} 
											j = LENGTH_len[(length_footer) = (int)i]; 
											bitbuf <<= (byte)j; 
											bitsleft -= (byte)j; 
										} 
										while (false);

										match_length += length_footer;
									}
									match_length += (2);

									match_offset = (ulong)(main_element >> 3);

									if (match_offset > 2) 
									{	                                
										if (match_offset != 3) 
										{
											extra = extra_bits[match_offset];
											do 
											{ 
											while (bitsleft < (extra)) 
											{ 
												bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
												bitsleft += 16; 
											} 
												verbatim_bits = (int)(bitbuf >> (ULONG_BITS_VALUE- (extra))); 
												bitbuf <<= extra; 
												bitsleft -= extra; 
											} 
											while (false);
											match_offset = position_base[match_offset] - 2 + (ulong)verbatim_bits;
										}
										else 
										{
											match_offset = 1;
										}
	                            
										R2 = R1; R1 = R0; R0 = match_offset;
									}
									else if (match_offset == 0) 
									{
										match_offset = R0;
									}
									else if (match_offset == 1) 
									{
										match_offset = R1;
										R1 = R0; R0 = match_offset;
									}
									else  
									{
										match_offset = R2;
										R2 = R0; R0 = match_offset;
									}

									rundest = window_posn;
									// rundest= window+window_posn
									runsrc  = rundest - match_offset;								
									window_posn += (ulong)match_length;
									this_run -= match_length;
	                        
									// runsrc < window
									while ((runsrc<0) && (match_length-- > 0))
									{
										window[rundest++]=window[runsrc+window_size];
										// *rundest++ = *(runsrc + window_size); 
										runsrc++;
									}
	                        
									while (match_length-- > 0) 
									{
										window[rundest++]=window[runsrc++];
										// *rundest++ = *runsrc++;
									}
								}
							}
							break;

						case LZX_BLOCKTYPE_ALIGNED:
							while (this_run > 0) 
							{
								do 
								{ 
								while (bitsleft < (16)) 
								{ 
									bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
									bitsleft += 16; 									
								} 
									hufftbl = MAINTREE_table; 
									if ((i = hufftbl[(bitbuf >> (ULONG_BITS_VALUE- 12))]) >= (((256) + 50*8))) 
									{ 
										j = (ulong)1 << (ULONG_BITS_VALUE- ((12))); 
										do 
										{ 
											j >>= 1; 
											i <<= 1; 
											if ((bitbuf & j)!=0) 
												i|=1; 
											else 
												i|=0; 
											if (j==0) 
											{ 
												return (2); 
											} 
										} 
										while ((i = hufftbl[i]) >= (((256) + 50*8))); 
									} 
									j = MAINTREE_len[(main_element) = (int)i]; 
									bitbuf <<= (int)j; 
									bitsleft -= (int)j; 
								} 
								while (false);

								if (main_element < (256)) 
								{                            
									window[window_posn++] = (byte)main_element;
									this_run--;
								}
								else 
								{                            
									main_element -= (256);
									match_length = main_element & (7);
									if (match_length == (7)) 
									{
										do 
										{ 
										while (bitsleft < (16)) 
										{ 
											bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
											bitsleft += 16; 
										} 
											hufftbl = LENGTH_table; 
											if ((i = hufftbl[(bitbuf >> (ULONG_BITS_VALUE- 12))]) >= (((249)+1))) 
											{ 
												j = (ulong) 1 << (ULONG_BITS_VALUE- ((12))); 
												do 
												{ 
													j >>= 1; 
													i <<= 1; 
													if ((bitbuf & j)!=0)
														i|=1;
													else
														i|=0;

													if (j==0) 
													{ 
														return (2); 
													} 
												} 
												while ((i = hufftbl[i]) >= (((249)+1))); 
											} 
											j = LENGTH_len[length_footer = (int)i]; 
											bitbuf <<= (int)j; 
											bitsleft -= (int)j; 
										} 
										while (false);
										match_length += length_footer;
									}
									match_length += (2);

									match_offset = (ulong)(main_element >> 3);

									if (match_offset > 2) 
									{                                
										extra = extra_bits[match_offset];
										match_offset = position_base[match_offset] - 2;
										if (extra > 3) 
										{                                    
											extra -= 3;
											do 
											{ 
											while (bitsleft < (extra)) 
											{	
												bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
												bitsleft += 16; 
											} 
												verbatim_bits = (int)(bitbuf >> (ULONG_BITS_VALUE- (extra))); 
												bitbuf <<= extra; 
												bitsleft -= extra; 
											} 
											while (false);
											match_offset += (ulong)(verbatim_bits << 3);
											do 
											{ 
											while (bitsleft < (16)) 
											{ 
												bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
												bitsleft += 16; 
											} 
												hufftbl = (ALIGNED_table); 
												if ((i = hufftbl[(bitbuf >> (ULONG_BITS_VALUE- 7))]) >= 8) 
												{ 
													j = (ulong)1 << (ULONG_BITS_VALUE- ((7))); 
													do 
													{ 
														j >>= 1; 
														i <<= 1; 
														if ((bitbuf & j)!=0)
															i|=1;
														else
															i|=0;
														if (j==0) 
														{ 
															return (2); 
														} 
													} 
													while ((i = hufftbl[i]) >= (((8)))); 
												} 

												j = (ALIGNED_len)[(aligned_bits) = (int)i]; 
												bitbuf <<= (int)j; 
												bitsleft -= (int)j; 
											} 
											while (false);
											match_offset += (ulong)aligned_bits;
										}
										else if (extra == 3)	
										{                                    
											do 
											{ 
											while (bitsleft < (16)) 
											{ 
												bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
												bitsleft += 16; 
											} 
												hufftbl = (ALIGNED_table); 
												if ((i = hufftbl[(bitbuf >> (ULONG_BITS_VALUE- 7))]) >= 8) 
												{ 
													j = (ulong)1 << (ULONG_BITS_VALUE- ((7))); 
													do 
													{ 
														j >>= 1; 
														i <<= 1; 
														if ((bitbuf & j)!=0)
															i|=1;
														else
															i|=0;
														if (j!=0) 
														{ 
															return (2); 
														} 
													} 
													while ((i = hufftbl[i]) >= 8); 
												} 
												j = (ALIGNED_len)[(aligned_bits) = (int)i]; 
												bitbuf <<= (int)j; 
												bitsleft -= (int)j; 
											} 
											while (false);
											match_offset += (ulong)aligned_bits;
										}
										else if (extra > 0) 
										{                                     
											do 
											{ 
											while (bitsleft < (extra)) 
											{ 
												bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS_VALUE - 16 - bitsleft); 
												bitsleft += 16; 
											} 
												(verbatim_bits) = (int)(bitbuf >> (int)(ULONG_BITS_VALUE- (extra))); 
												bitbuf <<= extra;
												bitsleft -= extra; 
											} 
											while (false);
											match_offset += (ulong)verbatim_bits;
										}
										else  
										{                                    
											match_offset = 1;
										}
                                
										R2 = R1; R1 = R0; R0 = match_offset;
									}
									else if (match_offset == 0) 
									{
										match_offset = R0;
									}
									else if (match_offset == 1) 
									{
										match_offset = R1;
										R1 = R0; R0 = match_offset;
									}
									else  
									{
										match_offset = R2;
										R2 = R0; R0 = match_offset;
									}

									rundest = window_posn;
									runsrc  = rundest - match_offset;
									window_posn += (ulong)match_length;
									this_run -= match_length;
	                            
									while ((runsrc<0) && (match_length-- > 0))
									{
										// *rundest++ = *(runsrc + window_size); runsrc++;
										window[rundest++]=window[runsrc + window_size];
										runsrc++;
									}
	                            
									while (match_length-- > 0) 
									{
										// *rundest++ = *runsrc++;
										window[rundest++]=window[runsrc++];
									}
								}
							}
							break;

						case LZX_BLOCKTYPE_UNCOMPRESSED:
							if ((BitSource.Position + (long)this_run) > (long)endinp) 
								return (2);

							// memcpy(window + window_posn, inposCount, this_run);
							for(i=0; i<(ulong)this_run;i++)
							{
								window[window_posn+i]=BitSource.ReadByte();
							}
							window_posn += (ulong)this_run;
							break;

						default:
							return DECR_ILLEGALDATA; /* might as well */
					}

				}
			}

			if (togo != 0) return DECR_ILLEGALDATA;

			// memcpy(outpos, window + ((!window_posn) ? window_size : window_posn) - outlen, (size_t) outlen);
			ulong start=0;
			if (window_posn==0)
				start=(ulong)window_size;
			else
				start=(ulong)window_posn;
			
			start-=(ulong)outlen;

			long Pos=OutputStream.Position;
			for(i=0;i<(ulong)outlen;i++)
			{	
				OutputStream.WriteByte(window[start+i]);
			}
			OutputStream.Seek(Pos,System.IO.SeekOrigin.Begin);
			
			/* intel E8 decoding */
			if ((frames_read++ < 32768) && intel_filesize != 0) 
			{
				if (outlen <= 6 || (intel_started==0)) 
				{
					intel_curpos += (long)outlen;
				}
				else 
				{
					// UBYTE *data    = outpos;
					long dataend = OutputStream.Position + (int)outlen - 10;
					long curpos    = intel_curpos;
					long filesize  = intel_filesize;
					long abs_off, rel_off;

					intel_curpos = (long)curpos + (long)outlen;

					while (OutputStream.Position < dataend) 
					{
						if (OutputStream.ReadByte() != 0xE8) 
						{ 
							curpos++; 
							continue; 
						}

						abs_off = (long)(OutputStream.ReadByte() | (OutputStream.ReadByte() <<8) | (OutputStream.ReadByte() <<16) | (OutputStream.ReadByte() <<24));
						if (abs_off < filesize)
						{
							if (abs_off >= 0)
								rel_off = (long)(abs_off - curpos);
							else 
								rel_off = (long)abs_off + filesize;
							OutputStream.WriteByte((byte)(rel_off  & 0x000000ff));
							OutputStream.WriteByte((byte)((rel_off & 0x0000ff00)>>8));
							OutputStream.WriteByte((byte)((rel_off & 0x00ff0000)>>16));
							OutputStream.WriteByte((byte)((rel_off & 0xff000000)>>24));							
						}
						curpos += 5;
					}
				}
			}			
			
			return DECR_OK;
		}		
	}
}
